package com.copractice.data.dao.hibernate;

import java.io.Serializable;
import java.util.List;

import org.hibernate.Query;
import org.hibernate.Session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.copractice.data.dao.GenericDAO;
import com.copractice.data.dao.hibernate.util.HibernateUtil;
import com.copractice.data.entity.PersistentObject;

/**
 * Represents hibernate implementation of <code>GenericDAO</code> interface.
 *
 * @author Dmitry Churbanov (dmitry.churbanov@gmail.com)
 * @version 1.0 03/13/2009
 */
public abstract class HibernateDAO<T extends PersistentObject, ID extends Serializable> implements GenericDAO<T, ID> {

    private static final Logger LOG = LoggerFactory.getLogger(HibernateDAO.class);
    
    /* Persistence class of particular <code>HibernateDAO</code> implementation */
    private Class<T> persistentClass = getPersistenceClass();
    
    private Session session;
    
    public HibernateDAO() {
        if(LOG.isDebugEnabled()) {
            LOG.debug("Create instance of HibernateDAO for class: {}", persistentClass);
        }
    }
    
    /**
     * Returns persistence class of particular <code>HibernateDAO</code> implementation
     *
     * @return Instances of the class <code>Class</code> of particular <code>HibernateDAO</code> implementation
     */
    protected abstract Class<T> getPersistenceClass();
    
    /**
     * Returns <code>org.hibernate.Session</code> class for current instance of <code>HibernateDAO</code>
     * 
     * @return <code>org.hibernate.Session</code> class for current instance of <code>HibernateDAO</code>
     */
    protected Session getSession() {
        if (session == null) {
            session = HibernateUtil.getSessionFactory().getCurrentSession();
            
            if(LOG.isDebugEnabled()) {
                LOG.debug("Gets new session for class: {}", persistentClass);
            }
        }
        return session;
    }    
    
    /**
     * Sets <code>org.hibernate.Session</code> class for current instance of <code>HibernateDAO</code>
     * 
     * @param session <code>org.hibernate.Session</code> class for current instance of <code>HibernateDAO</code>
     */
    public void setSession(Session session) {
        this.session = session;
    }
    
    @SuppressWarnings("unchecked")
    /**
     * @see com.copractice.data.dao.GenericDAO#getById
     */
    public T getById(ID id) {
        if(LOG.isDebugEnabled()) {
            LOG.debug("Gets '{}' entity by its id: {}", persistentClass, id);
        }
        
        return (T) getSession().get(getPersistenceClass(), id);
    }
    
    /**
     * @see com.copractice.data.dao.GenericDAO#findAll
     */
    @SuppressWarnings("unchecked")
    public List<T> findAll() {
        Query q = getSession().createQuery(getGeneralQueryString());
        
        if(LOG.isDebugEnabled()) {
            LOG.debug("Gets all '{}' entities", persistentClass);
        }
        
        return (List<T>) q.list();
    }
    
    /**
     * @see com.copractice.data.dao.GenericDAO#find
     */
    @SuppressWarnings("unchecked")
    public List<T> find(int firstResult, int maxResults) {
        Query q = getSession().createQuery(getGeneralQueryString());
        q.setFirstResult(firstResult);
        q.setMaxResults(maxResults);
        
        if(LOG.isDebugEnabled()) {
            LOG.debug("Gets all '{}' entities from {} to {}", new Object[]{persistentClass, firstResult, maxResults});
        }
        
        return (List<T>) q.list();
    }
    
    /**
     * @see com.copractice.data.dao.GenericDAO#save
     */
    public void save(T entity) {
        if(LOG.isDebugEnabled()) {
            LOG.debug("Saves '{}' entity: {}", persistentClass, entity);
        }
        
        getSession().saveOrUpdate(entity);
    }

    /**
     * @see com.copractice.data.dao.GenericDAO#update
     */
    public void update(T entity) {
        if(LOG.isDebugEnabled()) {
            LOG.debug("Updates '{}' entity: {}", persistentClass, entity);
        }
        
        getSession().saveOrUpdate(entity);
    }
    
    /**
     * @see com.copractice.data.dao.GenericDAO#merge
     */
    public void merge(T entity) {
        if(LOG.isDebugEnabled()) {
            LOG.debug("Merges '{}' entity: {}", persistentClass, entity);
        }
        
        getSession().merge(entity);
    }
    
    /**
     * @see com.copractice.data.dao.GenericDAO#delete
     */
    public void delete(T entity) {
        if(LOG.isDebugEnabled()) {
            LOG.debug("Deletes '{}' entity: {}", persistentClass, entity);
        }
        
        getSession().delete(entity);
    }
    
    /**
     * @see com.copractice.data.dao.GenericDAO#deleteById
     */
    public void deleteById(ID id) {
        if(LOG.isDebugEnabled()) {
            LOG.debug("Deletes '{}' entity by its id: {}", persistentClass, id);
        }
        
        T entity = getById(id);
        if (entity != null) {
            delete(entity);
        }
    }
    
    /**
     * Creates and returns query string of the next view: 'from ClassName class';
     * Where ClassName is a name of the entity class.
     * 
     * @return general query string
     */
    private String getGeneralQueryString() {
        return "from " + getPersistenceClass().getName() + " clazz";
    }
    
}
